<?php

    class XmlNodeRoot extends XmlNode implements ArrayAccess
    {
        protected $childs = array();
        protected $childsSequence = array();

        /**
         * Returns count childs with specified name.
         * If $childName is null returns count of all childs.
         *
         * @param string $childName
         * @return int
         */
        public function countChilds($childName = null)
        {
            $countChilds = 0;
            if (is_null($childName))
            {
                $countChilds = count($this->childsSequence);
            }
            else
            {
                $child = $this->getNodeByName($childName);
                $countChilds = $child ? $child->count() : 0;
            }
            return $countChilds;
        }

        /**
         * Adds child to node.
         *
         * @param XmlNode $child
         */
        public function addChild(XmlNode $child)
        {
            $nodeName = $child->getName();
            if (isset($this->childs[$nodeName]))
            {
                $this->childs[$nodeName]->addBrother($child);
            }
            else
            {
                $this->childs[$nodeName] = $child;
            }

            $this->childsSequence[] = $child;
        }

        /**
         * Returns child node by it index.
         *
         * @param int $childIndex
         * @return XmlNode
         */
        public function getChildAt($childIndex)
        {
            return isset($this->childsSequence[$childIndex]) ? $this->childsSequence[$childIndex] : null;
        }

        /**
         * Returns child node by it name.
         *
         * @param string $name
         * @return XmlNode
         */
        private function getNodeByName($nodeName)
        {
            $node = null;
            $childNodeName = null;

            $slashIndex = strpos($nodeName, "/");
            if ($slashIndex !== false)
            {
                $childNodeName = substr($nodeName, $slashIndex + 1);
                $nodeName = substr($nodeName, 0, $slashIndex);
            }

            $matches = array();
            if (preg_match("/^(.+)\\[(\d+)\\]$/", $nodeName, $matches))
            {
                $nodeName   = $matches[1];
                $childIndex = $matches[2];

                $node = isset($this->childs[$nodeName]) ? $this->childs[$nodeName] : null;
                if ($node)
                {
                    $node = $node->get($childIndex);
                }
            }
            else
            {
                $node = isset($this->childs[$nodeName]) ? $this->childs[$nodeName] : null;
            }

            if ($node && $childNodeName)
            {
                return ($node instanceof XmlNodeRoot) ? $node[$childNodeName] : null;
            }
            return $node;
        }

        /**
         * Converts xml node to array.
         *
         * @return array
         */
        public function toArray()
        {
            if ( !$this->childs )
            {
                return parent::toArray();
            }

            $childs = array();
            foreach ( $this->childs as $childNode )
            {
                $childs = array_merge( $childs, $childNode->toArray() );
            }
            return array( $this->getName() => $childs );
        }

        /* Implementation of ArrayAccess methods */

        public function offsetExists($offset)
        {
            return !is_null($this->getNodeByName($offset));
        }

        public function offsetGet($offset)
        {
            return $this->getNodeByName($offset);
        }

        public function offsetSet($offset, $value)
        {
            //not allow to dynamically changing nodes
        }

        public function offsetUnset($offset)
        {
            //not allow to dynamically removing nodes
        }
    }

?>